
# Copyright 2011 radialmind.org

from google.appengine.ext import db

"""Database models used in the R/C Fly spot application.

  FlySpot: Stores user fly spot events.
  Settings: Stores various user settings not stored in the actual users API.
"""
import geobox
import logging
import math

RADIUS = 6378100

GEOBOX_CONFIGS = (
  (4, 5, True),
  (3, 2, True),
  (3, 8, False),
  (3, 16, False),
  (2, 5, False),
)

def _earth_distance(lat1, lon1, lat2, lon2):
  lat1, lon1 = math.radians(float(lat1)), math.radians(float(lon1))
  lat2, lon2 = math.radians(float(lat2)), math.radians(float(lon2))
  return RADIUS * math.acos(math.sin(lat1) * math.sin(lat2) +
      math.cos(lat1) * math.cos(lat2) * math.cos(lon2 - lon1))


class FlySpot(db.Model):
  timestamp = db.DateTimeProperty(auto_now_add = True)
  user = db.UserProperty()
  spotname = db.StringProperty()
  location = db.GeoPtProperty()
  experience = db.StringProperty()
  rating = db.RatingProperty()
  videolink = db.LinkProperty()
  geoboxes = db.StringListProperty()

  @classmethod
  def add(self, **kwargs):
    lat = kwargs.pop('lat')
    lon = kwargs.pop('lon')
    location = db.GeoPt(lat, lon)
    spotname = kwargs['spotname']
    user = kwargs['user']

    new_spot = FlySpot(user=user, spotname=spotname, location=location)
    all_boxes = []
    for (resolution, slice, use_set) in GEOBOX_CONFIGS:
      if use_set:
        all_boxes.extend(geobox.compute_set(lat, lon, resolution, slice))
      else:
        all_boxes.append(geobox.compute(lat, lon, resolution, slice))
    new_spot.geoboxes = all_boxes
    new_spot.experience = kwargs['experience']
    # new_spot.rating = kwargs['rating']
    # new_spot.videolink = kwargs['videolink']
    new_spot.put()

  @classmethod
  def query(self, lat, lon, max_results, min_params):
    """Queries for locations repeatedly until max results or scope is reached.
    Args:
    lat, lon: Coordinates of the agent querying.
    max_results: Maximum number of stops to find.
    min_params: Tuple (resolution, slice) of the minimum resolution to allow.

    Returns:
    List of (distance, store) tuples, ordered by minimum distance first.
    There will be no duplicates in these results. Distance is in meters.
    """
    found_spots = []

    for params in GEOBOX_CONFIGS:
      if len(found_spots) >= max_results:
        break
      if params < min_params:
        break

      resolution, slice, unused = params
      box = geobox.compute(lat, lon, resolution, slice)
      logging.info("Searching for box=%s at resolution=%s, slice=%s",
         box, resolution, slice)
      query = self.all().filter("geoboxes =", box)
      results = query.fetch( 50 )
      logging.info("Found %d results", len(results))
      for result in results:
        found_spots.append( result )

    spots_by_distance = []
    for spot in found_spots:
       distance = _earth_distance(lat, lon, spot.location.lat, spot.location.lon)
       spots_by_distance.append((distance, spot))
    spots_by_distance.sort()

    return spots_by_distance

class SerSpot(db.Model):
  user = db.StringProperty()
  spotname = db.StringProperty()
  lat = db.FloatProperty()
  lon = db.FloatProperty()
  experience = db.StringProperty()
  rating = db.RatingProperty()
  videolink = db.LinkProperty()

class Settings(db.Model):
  user = db.UserProperty()
  default_location = db.StringProperty(default = 'Area 51, Nevada, United States')
  default_zoom = db.IntegerProperty(default = 13)


